package be.hogent.iii.project.objects;

import be.hogent.iii.project.objects.gebouwen.Gebouw;
import be.hogent.iii.project.objects.ventjes.Ventje;
import be.hogent.iii.project.objects.vakken.Vak;
import be.hogent.iii.project.objects.ventjes.Barbaar;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class Veld implements Serializable {

    private static Coordinaat[] mogelijkeVerplaatsingen = {
        new Coordinaat(-1, 0), new Coordinaat(0, 1), new Coordinaat(1, 0),
        new Coordinaat(0, -1), new Coordinaat(-1, 1), new Coordinaat(1, 1),
        new Coordinaat(1, -1), new Coordinaat(-1, -1)
    };
    private static int aantalMogelijkeVerplaatsingen = 8;
    private Vak[][] speelveld;
    private String mapnaam;
    private int grootteX;
    private int grootteY;
    private ArrayList<Entiteit> entiteiten;
    private int spelerAanBeurt;

    public Veld() {

    }

    public Veld(int grootteX, int grootteY) {
        this.grootteX = grootteX;
        this.grootteY = grootteY;
        speelveld = new Vak[grootteX][grootteY];
        entiteiten = new ArrayList<Entiteit>();
    }

    public ArrayList<Entiteit> getEntiteiten() {
        return entiteiten;
    }

    public void addEntiteit(Entiteit ob) {
        entiteiten.add(ob);
    }

    public int getGrootteX() {
        return grootteX;
    }

    public int getGrootteY() {
        return grootteY;
    }

    public void setGrootteX(int val) {
        this.grootteX = val;
    }

    public void setGrootteY(int val) {
        this.grootteY = val;
    }

    public String getMapnaam() {
        return mapnaam;
    }

    public void setMapnaam(String val) {
        this.mapnaam = val;
    }

    public Vak getVakje(int i, int j) {
        return speelveld[i][j];
    }

    public Vak getVakje(Coordinaat c) {
        return speelveld[c.getX()][c.getY()];
    }

    public void setVakje(int i, int j, Vak vakje) {
        speelveld[i][j] = vakje;
    }

    /**
     * bewegingen uitvoeren die gemaakt zijn
     * en aanvallen uitwerken
     */
    public void eindeBeurt() {
        for (int i = 0; i < entiteiten.size(); i++) {
            Entiteit item = entiteiten.get(i);
            boolean[][] aanvalPlaatsen = getAanvalsPlaatsen(spelerAanBeurt);
            if (item.getClass().getPackage().getName().equalsIgnoreCase("be.hogent.iii.project.objects.ventjes")) {
                Ventje ventje = (Ventje) item;
                if (ventje.getBeweging() != null) {
                    if (aanvalPlaatsen[ventje.getBeweging().getX()][ventje.getBeweging().getY()]) {
                        verwerkAanval(ventje);
                    } else {
                        ventje.setPlaats(ventje.getBeweging());
                        ventje.setBeweging(null);
                    }
                }
            }
        }
    }

    public void verwerkAanval(Ventje ventje) {
        Coordinaat resultaat;
        Coordinaat naar = ventje.getBeweging();
        Coordinaat van = ventje.getPlaats();
        
        ArrayList<Ventje> ventjes = new ArrayList<Ventje>();
        ArrayList<Entiteit> ventjesOpZelfdePlaats = getEntiteiten(van);
        for(int i=0;i<ventjesOpZelfdePlaats.size();i++){
            Ventje nieuwVentje = (Ventje)ventjesOpZelfdePlaats.get(i);
            if (nieuwVentje.getBeweging().isGelijkWaardig(naar))
                ventjes.add(nieuwVentje);
        }
        boolean[][] geldigePlaatsen = getGeldigePlaatsen(ventjes);
        int[][] afstanden = bepaalAfstandenTabel(geldigePlaatsen, van);
        //aanvalsbeweging
        Coordinaat dichtstLiggende = null;
        int dichtstePlaats = -1;
        for (int i = 0; i < aantalMogelijkeVerplaatsingen; i++) {
            if (isVerplaatsingBinnenVeld(naar, mogelijkeVerplaatsingen[i])) {
                int nieuweAfstand = afstanden[naar.getX() + mogelijkeVerplaatsingen[i].getX()][naar.getY() + mogelijkeVerplaatsingen[i].getY()];
                if (isVerplaatsingBinnenVeld(naar, mogelijkeVerplaatsingen[i]) && nieuweAfstand != 0 && (dichtstePlaats == -1 || dichtstePlaats > nieuweAfstand)) {
                    dichtstePlaats = afstanden[naar.getX() + mogelijkeVerplaatsingen[i].getX()][naar.getY() + mogelijkeVerplaatsingen[i].getY()];
                    dichtstLiggende = mogelijkeVerplaatsingen[i];
                }
            }
        }
        if (dichtstLiggende != null) {
            System.out.println("dichtsbijzijnde plaats gevonden: " + dichtstLiggende.getX() + "  " + dichtstLiggende.getY());
            resultaat = new Coordinaat(naar.getX() + dichtstLiggende.getX(), naar.getY() + dichtstLiggende.getY());
            ArrayList<Entiteit> verdediging = getEntiteiten(naar);
            // Aanval afwerken !!!! ....
            for (int i = 0; i < ventjes.size(); i++) {
                if (verdediging.size() > 0) {
                    Entiteit vangtSlagOp = verdediging.get(0);
                    int aanval = ((Ventje) ventjes.get(i)).getAanvalsSterkte();
                    int overblijvendeHitpoints = vangtSlagOp.getHitpoints() - aanval;
                    if (overblijvendeHitpoints <= 0) {
                        System.out.println("ventje gestorven");
                        //ventje is dood, verwijderen
                        verdediging.remove(vangtSlagOp);
                        entiteiten.remove(vangtSlagOp);
                    } else {
                        vangtSlagOp.setHitpoints(overblijvendeHitpoints);
                    }
                }
            }
            for (int i = 0; i < ventjes.size(); i++) {
                if (verdediging.size() == 0) {
                    ventjes.get(i).setPlaats(naar);
                    ventjes.get(i).setBeweging(null);
                } else {
                    ventjes.get(i).setPlaats(resultaat);
                    ventjes.get(i).setBeweging(null);
                }
            }
        }
    }
    

    /**
     * ventjes maken van speler spelerId
     */
    public void beginBeurt(int spelerId) {
        spelerAanBeurt = spelerId;
        for (int i = 0; i < entiteiten.size(); i++) {
            Entiteit item = entiteiten.get(i);
            //ook niet hardcoderen
            if (item.getEigenaarId() == spelerId && item.getClass().getPackage().getName().equalsIgnoreCase("be.hogent.iii.project.objects.gebouwen")) {
                //nieuw ventje aanmaken voor gebouw
                Coordinaat plaatsNieuwVentje = ((Gebouw) item).getRallyPoint();
                Ventje nieuwVentje = new Barbaar();
                nieuwVentje.setPlaats(plaatsNieuwVentje);
                nieuwVentje.setEigenaarId(spelerId);
                entiteiten.add(nieuwVentje);
            }
        }
    }

    //deze twee methodes zijn dubbel code, pas dit eens aan
    public ArrayList<Entiteit> getEntiteiten(Coordinaat plaatsNodig) {
        ArrayList<Entiteit> nodig = new ArrayList<Entiteit>();
        for (int i = 0; i < entiteiten.size(); i++) {
            Entiteit e = entiteiten.get(i);
            Coordinaat c = e.getPlaats();
            if (c.getX() == plaatsNodig.getX() && c.getY() == plaatsNodig.getY()) {
                nodig.add(e);
            }
        }
        return nodig;
    }

    public ArrayList<Entiteit> getEntiteiten(int x, int y) {
        return getEntiteiten(new Coordinaat(x, y));
    }

    //beetje commentaar om methode uit te leggen is welkom
    /*public Coordinaat actieValAan(int spelerId, Coordinaat van, Coordinaat naar) {
    ArrayList<Entiteit> ventjes = getEntiteiten(van);
    System.out.println("Probeer iets aan te vallen");
    Coordinaat resultaat = naar;
    int maxAfstand = bepaalMinsteStappen(ventjes);
    System.out.println("max stappen berekend: " + maxAfstand);
    int[][] geldigePlaatsen = getGeldigePlaatsen(((Ventje) ventjes.get(0)).getEigenaarId());
    int[][] afstanden = bepaalAfstandenTabel(geldigePlaatsen, ((Ventje) ventjes.get(0)).getEigenaarId(),
    ((Ventje) ventjes.get(0)).getPlaats(), naar);
    if (geldigePlaatsen[naar.getX()][naar.getY()] == 2 && afstanden[naar.getX()][naar.getY()] <= maxAfstand && !((Ventje) ventjes.get(0)).isHeeftAangevallen()) {
    System.out.println("2de deel van aanval");
    //aanvalsbeweging
    Coordinaat dichtstLiggende = null;
    int dichtstePlaats = -1;
    for (int i = 0; i < aantalMogelijkeVerplaatsingen; i++) {
    if (isVerplaatsingBinnenVeld(naar, mogelijkeVerplaatsingen[i])) {
    int nieuweAfstand = afstanden[naar.getX() + mogelijkeVerplaatsingen[i].getX()][naar.getY() + mogelijkeVerplaatsingen[i].getY()];
    if (isVerplaatsingBinnenVeld(naar, mogelijkeVerplaatsingen[i]) && nieuweAfstand != 0 && (dichtstePlaats == -1 || dichtstePlaats > nieuweAfstand)) {
    dichtstePlaats = afstanden[naar.getX() + mogelijkeVerplaatsingen[i].getX()][naar.getY() + mogelijkeVerplaatsingen[i].getY()];
    dichtstLiggende = mogelijkeVerplaatsingen[i];
    }
    }
    }
    if (dichtstLiggende != null) {
    System.out.println("dichtsbijzijnde plaats gevonden: " + dichtstLiggende.getX() + "  " + dichtstLiggende.getY());
    resultaat = new Coordinaat(naar.getX() + dichtstLiggende.getX(), naar.getY() + dichtstLiggende.getY());
    ArrayList<Entiteit> verdediging = getEntiteiten(naar);
    // Aanval afwerken !!!! ....
    for (int i = 0; i < ventjes.size(); i++) {
    if (verdediging.size() > 0) {
    Entiteit vangtSlagOp = verdediging.get(0);
    int aanval = ((Ventje) ventjes.get(i)).getAanvalsSterkte();
    int overblijvendeHitpoints = vangtSlagOp.getHitpoints() - aanval;
    if (overblijvendeHitpoints <= 0) {
    System.out.println("ventje gestorven");
    //ventje is dood, verwijderen
    verdediging.remove(vangtSlagOp);
    entiteiten.remove(vangtSlagOp);
    } else {
    vangtSlagOp.setHitpoints(overblijvendeHitpoints);
    }
    }
    }
    for (int i = 0; i < ventjes.size(); i++) {
    //((Ventje) ventjes.get(i)).setHeeftAangevallen(true);
    if (verdediging.size() == 0) {
    ventjes.get(i).setPlaats(naar);
    } else {
    ventjes.get(i).setPlaats(resultaat);
    }
    }
    if (verdediging.size() == 0) {
    return naar;
    }
    }
    } else {
    return van;
    }
    return resultaat;
    }*/

    //public boolean actieVerplaats(List ventjes, Coordinaat naar) {
    //beetje commentaar om methode uit te leggen is welkom
    public ArrayList<Coordinaat> actieVerplaats(ArrayList<Entiteit> entiteiten, Coordinaat naar) {
        ArrayList<Ventje> ventjes = new ArrayList<Ventje>();
        for (int i = 0; i < entiteiten.size(); i++) {
            if (entiteiten.get(i).getClass().getPackage().getName().equalsIgnoreCase("be.hogent.iii.project.objects.ventjes")) {
                ventjes.add((Ventje) entiteiten.get(i));
            }
        }
        System.out.println("" + ventjes.size());
        int maxAfstand = bepaalMinsteStappen(ventjes);
        boolean[][] geldigePlaatsen = getGeldigePlaatsen(ventjes);
        Coordinaat van = ventjes.get(0).getPlaats();
        int[][] afstanden = bepaalAfstandenTabel(geldigePlaatsen, van);
        boolean[][] aanvalPlaatsen = getAanvalsPlaatsen(spelerAanBeurt);

        //correctie doen indien de eindbestemming een aanvalsplaats is
        if (afstanden[naar.getX()][naar.getY()] == 0 && aanvalPlaatsen[naar.getX()][naar.getY()]) {
            for (int k = 0; k < aantalMogelijkeVerplaatsingen; k++) {
                if (isVerplaatsingBinnenVeld(naar, mogelijkeVerplaatsingen[k])) {
                    Coordinaat buur = naar.somMet(mogelijkeVerplaatsingen[k]);
                    if ((afstanden[buur.getX()][buur.getY()] + 1 < afstanden[naar.getX()][naar.getY()] || afstanden[naar.getX()][naar.getY()] == 0) && afstanden[buur.getX()][buur.getY()] != 0) {
                        afstanden[naar.getX()][naar.getY()] = afstanden[buur.getX()][buur.getY()] + 1;
                    }
                }
            }
        }

        //proberen ventjes verzetten naar nieuwe plaats
        if (afstanden[naar.getX()][naar.getY()] > 0 && maxAfstand >= afstanden[naar.getX()][naar.getY()]) {


            for (int i = 0; i < ventjes.size(); i++) {
                ((Ventje) ventjes.get(i)).setBeweging(naar);
            }
            System.out.println("beweging opgeslagen: aantal stappen: " + afstanden[naar.getX()][naar.getY()]);

            ArrayList<Coordinaat> pad = new ArrayList<Coordinaat>();

            Coordinaat vorige = new Coordinaat(naar.getX(), naar.getY());
            pad.add(vorige);
            for (int i = afstanden[naar.getX()][naar.getY()]; i > 1; i--) {
                //while (afstanden[vorige.getX()][vorige.getY()] > 0){
                boolean gevonden = false;
                for (int k = 0; k < aantalMogelijkeVerplaatsingen; k++) {
                    if (isVerplaatsingBinnenVeld(vorige, mogelijkeVerplaatsingen[k]) && !gevonden) {
                        Coordinaat nieuw = new Coordinaat(vorige.getX() + mogelijkeVerplaatsingen[k].getX(), vorige.getY() + mogelijkeVerplaatsingen[k].getY());
                        if (afstanden[nieuw.getX()][nieuw.getY()] == afstanden[vorige.getX()][vorige.getY()] - 1) {
                            vorige = nieuw;
                            pad.add(nieuw);
                            gevonden = true;
                        //System.out.println("padlengte: " + pad.size());
                        }
                    }
                }
            }

            return pad;
        } else {
            //geen verplaatsing
            for (int i = 0; i < ventjes.size(); i++) {
                ((Ventje) ventjes.get(i)).setBeweging(null);
            }
            System.out.println("De afstand is te ver");
            return null;
        }
    }

    private boolean isVerplaatsingBinnenVeld(Coordinaat positie, Coordinaat verplaatsing) {
        if (positie.getX() + verplaatsing.getX() < 0 || positie.getX() + verplaatsing.getX() >= grootteX) {
            return false;
        } else if (positie.getY() + verplaatsing.getY() < 0 || positie.getY() + verplaatsing.getY() >= grootteY) {
            return false;
        } else {
            return true;
        }
    }

    private int bepaalMinsteStappen(List ventjes) {
        int min = 0;
        if (!ventjes.isEmpty()) {
            min = ((Ventje) ventjes.get(0)).getAantalStappen();
            for (int i = 1; i < ventjes.size(); i++) {
                if (min > ((Ventje) ventjes.get(i)).getAantalStappen()) {
                    min = ((Ventje) ventjes.get(i)).getAantalStappen();
                }
            }
        }
        return min;
    }

    private boolean[][] getAanvalsPlaatsen(int spelersId) {
        boolean[][] plaatsen = new boolean[grootteX][grootteY];
        for (int i = 0; i < grootteX; i++) {
            for (int j = 0; j < grootteY; j++) {
                plaatsen[i][j] = false;
            }
        }
        for (int i = 0; i < entiteiten.size(); i++) {
            Entiteit item = entiteiten.get(i);
            //System.out.println("object checken: " + item.getClass().getName());
            if (item.getEigenaarId() != spelersId) {
                plaatsen[item.getPlaats().getX()][item.getPlaats().getY()] = true;
            }
        }
        return plaatsen;
    }

    /**
     * kijkt welke vakjes geldige plaatsen zijn om ventjes op te zetten van de speler
     * beter ineens alle vakjes opvragen omdat voor 1 vakje te controleren ook 
     * heel de lijst van objecten moet afgegaan worden.
     */
    private boolean[][] getGeldigePlaatsen(ArrayList<Ventje> ventjes) {
        BewegingsControleur controle = new BewegingsControleur();
        boolean[][] plaatsen = new boolean[grootteX][grootteY];
        for (int i = 0; i < grootteX; i++) {
            for (int j = 0; j < grootteY; j++) {
                //controle op basis van ventje en soort ondergrond
                if (controle.isGeldigeBeweging(null, speelveld[i][j])) {
                    plaatsen[i][j] = true;
                } else {
                    plaatsen[i][j] = false;
                }
            }
        }
        for (int i = 0; i < entiteiten.size(); i++) {
            Entiteit item = entiteiten.get(i);
            //System.out.println("object checken: " + item.getClass().getName());
            if (item.getEigenaarId() != ventjes.get(0).getEigenaarId()) {
                plaatsen[item.getPlaats().getX()][item.getPlaats().getY()] = false;
            } else if (item.getClass().getPackage().getName().equalsIgnoreCase("be.hogent.iii.project.objects.gebouwen")) {
                plaatsen[item.getPlaats().getX()][item.getPlaats().getY()] = false;
            }
        }
        /* geldigePlaatsenTabel uitschrijven
        for (int i = 0; i < grootteY; i++) {
        for (int j = 0; j < grootteX; j++) {
        System.out.print(" " + plaatsen[j][i]);
        }
        System.out.println();
        }
         */
        return plaatsen;
    }

    private int[][] bepaalAfstandenTabel(boolean[][] geldigeplaatsen, Coordinaat van) {
        int[][] afstand = new int[grootteX][grootteY];
        afstand[van.getX()][van.getY()] = 1;
        boolean verder = true;
        int afstandBereken = 1;


        System.out.println("geldige plaatsentabel verkregen");

        while (verder) {
            verder = false;

            //alle vakjes afgaan en kijken welke op afstand afstandBereken liggen
            //en bij buren afstand 1 hoger maken als afstandBereken
            for (int i = 0; i < grootteX; i++) {
                for (int j = 0; j < grootteY; j++) {
                    if (afstand[i][j] == afstandBereken) {
                        for (int k = 0; k < aantalMogelijkeVerplaatsingen; k++) {
                            int nieuwX = i + mogelijkeVerplaatsingen[k].getX();
                            int nieuwY = j + mogelijkeVerplaatsingen[k].getY();
                            if (isVerplaatsingBinnenVeld(new Coordinaat(i, j), mogelijkeVerplaatsingen[k]) && (afstand[nieuwX][nieuwY] > afstand[i][j] || afstand[nieuwX][nieuwY] == 0) && geldigeplaatsen[nieuwX][nieuwY]) {
                                afstand[nieuwX][nieuwY] = afstandBereken + 1;
                                verder = true;
                            }
                        }
                    }
                }
            }
            afstandBereken++;
        }

        //zorgen dat er ook terug naar de originele locatie kan gegaan worden indien in deze beurt al verplaats is
        afstand[van.getX()][van.getY()]++;

        for (int i = 0; i < grootteY; i++) {
            for (int j = 0; j < grootteX; j++) {
                if (afstand[j][i] > 0) {
                    afstand[j][i]--;
                }
            //System.out.print(" " + afstand[j][i]);
            }
        //System.out.println();
        }

        //System.out.println("" + naar.getX() + " " + naar.getY());
        //return afstand[naar.getX()][naar.getY()] - 1;
        return afstand;
    }
}

